package qc.module.iot.protocol;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qc.common.core.enums.iot.IotPacketsDataProtocolEnum;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 数据帧解析基类
 *
 * @author QuCheng Tech
 * @since 2025/11/5
 */
public class IotFrameParseBase implements IFrameParse {
    private static final Logger logger = LoggerFactory.getLogger(IotFrameParseBase.class);

    /**
     * 数据帧协议
     */
    protected IotPacketsDataProtocolEnum protocol;

    /**
     * 数据帧起始符字节数组
     */
    protected byte[] frameStartBytes;
    /**
     * 数据帧结束符字节数组，可以为空
     */
    protected byte[] frameEndBytes;
    /**
     * 数据帧最小长度，包含起始符和结束符的最小有效长度
     */
    protected int frameMinLength;

    public IotFrameParseBase() {
        this.protocol = IotPacketsDataProtocolEnum.UN_DEFINE;
        this.frameStartBytes = null;
        this.frameEndBytes = null;
        this.frameMinLength = 0x0;
    }

    public IotFrameParseBase(IotPacketsDataProtocolEnum protocol, byte[] frameStartBytes) {
        this.protocol = protocol;
        this.frameStartBytes = frameStartBytes;
        this.frameEndBytes = null;
        this.frameMinLength = 0x0;
        //如果指定的数据帧起始符字节数组长度＞0，默认设置数据帧的最小长度为起始符长度
        if (this.frameStartBytes != null && this.frameStartBytes.length > 0x0)
            this.frameMinLength = this.frameStartBytes.length;
    }

    public IotFrameParseBase(IotPacketsDataProtocolEnum protocol, byte[] frameStartBytes, int frameMinLength) {
        this.protocol = protocol;
        this.frameStartBytes = frameStartBytes;
        this.frameEndBytes = null;
        this.frameMinLength = 0x0;
        //如果指定的数据帧最小长度有效并且大于数据帧起始符长度使用指定的数据帧最小长度，否则默认使用起始符长度
        if (frameMinLength > 0x0)
            this.frameMinLength = frameMinLength;
        if (this.frameStartBytes != null && this.frameStartBytes.length > frameMinLength)
            this.frameMinLength = this.frameStartBytes.length;
    }

    public IotFrameParseBase(IotPacketsDataProtocolEnum protocol, byte[] frameStartBytes, byte[] frameEndBytes, int frameMinLength) {
        this.protocol = protocol;
        this.frameStartBytes = frameStartBytes;
        this.frameEndBytes = frameEndBytes;
        this.frameMinLength = 0x0;
        //如果指定的数据帧最小长度有效并且大于起始符+截止符的总长使用指定的数据帧最小航都，否则默认使用起始符+截止符的总长度
        if (frameMinLength > 0x0)
            this.frameMinLength = frameMinLength;
        int frameStartAndEndByteLength = 0x0;
        if (this.frameStartBytes != null && this.frameStartBytes.length > 0x0)
            frameStartAndEndByteLength += this.frameStartBytes.length;
        if (this.frameEndBytes != null && this.frameEndBytes.length > 0x0)
            frameStartAndEndByteLength += this.frameEndBytes.length;
        if (frameStartAndEndByteLength > frameMinLength)
            this.frameMinLength = frameStartAndEndByteLength;
    }

    /**
     * 根据报文字节数组分解出可能得数据帧
     * 提供一个通用的根据起始字符和结束字符（可以没有）分解报文的方法，可以给子类调用
     *
     * @param packetsBytes 报文字节数组
     * @return java.util.List<qc.module.iot.protocol.IotPacketsSplitResult>
     * @author QuCheng Tech
     * @since 2025/11/6
     */
    @Override
    public List<IotPacketsSplitResult> split(byte[] packetsBytes) {
        logger.debug("split");
        if (packetsBytes == null || packetsBytes.length < 0x1) {
            //如果传入的报文为空无法进行分解
            logger.debug("分解报文数据帧时报文内容为空");
        } else {
            if (this.frameMinLength > 0x0 && packetsBytes.length < this.frameMinLength) {
                //如果有传入最小数据帧长度，判断报文总长要≥最小数据帧长度
                logger.debug("分解报文数据帧时报文内容长度不够，报文总长度[{}]＜数据帧最小长度[{}]", packetsBytes.length, this.frameMinLength);
            } else {
                if (this.frameStartBytes == null || this.frameStartBytes.length < 0x1) {
                    //如果传入的起始符为空无法进行分解，结束符可以为空
                    logger.debug("分解报文数据帧时报文起始符为空，无法进行数据帧分解");
                } else {
                    //如果有结束符，从后往前获取最后出现的结束符；分解的报文结束位置索引为所有分解的报文公用，因此在前面先进行获取最后一个出现的结束符索引
                    //先初始化最后一个出现的结束符索引为传入报文总长度-1，如果传入了数据帧结束符判定后没有结束符时在设置索引为-1表示没有结束符
                    int lastFrameEndIndex = packetsBytes.length - 0x1;//位置索引从0开始，因此需要减1

                    if (this.frameEndBytes == null || this.frameEndBytes.length < 0x1) {
                        logger.debug("分解报文数据帧时，未指定结束符，使用报文长度设置数据帧结束位置索引为[{}]", lastFrameEndIndex);
                    } else {
                        //如果传入的有指定结束符，获取指定结束符最后一次出现的位置索引
                        //如果传入了数据帧结束符判定后没有结束符时在设置索引为-1表示没有结束符
                        lastFrameEndIndex = -0x1;
                        for (int i = packetsBytes.length - this.frameEndBytes.length; i > 0x0; i--) {
                            //先匹配第一个结束符字节相等
                            if (packetsBytes[i] == this.frameEndBytes[0x0]) {
                                //记录是否所有结束符均判断通过
                                boolean isFrameEndAllRight = true;
                                //如果结束符长度＞1，需要匹配从第一个结束符字节开始后面连续n个字节均相等
                                if (this.frameEndBytes.length > 0x1) {
                                    for (int j = 1; j < this.frameEndBytes.length; j++) {
                                        //如果已经有结束符中的任意一个字节不相等，表示结束符全部判等不通过，不需要再判断后面的结束符字节
                                        if (isFrameEndAllRight == true) {
                                            if (packetsBytes[i + j] == this.frameEndBytes[j]) {

                                            } else {
                                                //如果从第二个开始的结束符字节不相等，跳出本循环；多个结束符中的任意一个错误均判定为当前数据帧结束符错误
                                                isFrameEndAllRight = false;
                                                break;
                                            }
                                        }
                                    }
                                }

                                //如果所有结束符字节都相等，使用当前的结束符位置索引作为最后一次结束符出现的位置索引，跳出循环
                                if (isFrameEndAllRight == true) {
                                    logger.debug("分解报文数据帧时，根据指定的结束符得到的最后一次结束符位置索引为[{}]", i);
                                    lastFrameEndIndex = i;
                                    break;
                                }
                            }
                        }
                    }

                    //如果得到的结束位置索引无效，不需要进行后续的判断
                    if (lastFrameEndIndex < 0x0) {
                        //结束符位置索引不能为负值，为负值表示无效
                        logger.debug("分解报文数据帧时，得到数据帧结束位置索引错误，在报文中未找到指定的结束符");
                    } else {
                        //数据帧的完整结束位置索引=最后一次结束位置索引+结束符长度（如有）
                        int endIndex = lastFrameEndIndex;
                        if (this.frameEndBytes != null && this.frameEndBytes.length > 0x0)
                            endIndex = lastFrameEndIndex + this.frameEndBytes.length;

                        List<IotPacketsSplitResult> result = new ArrayList<>();

                        //遍历传输的报文，从第0个开始，至结束位置索引
                        for (int i = 0; i < lastFrameEndIndex; i++) {
                            //先匹配第一个起始符字节相等
                            if (packetsBytes[i] == this.frameStartBytes[0x0]) {
                                //记录是否所有起始符均判断通过，如果判断通过再判断最小长度
                                boolean isFrameStartAllRight = true;
                                //如果起始符长度＞1，需要匹配从第一个起始符开始后面连续n个字节均相等
                                if (this.frameStartBytes.length > 0x1) {
                                    //判断前需要保证传入的报文从当前索引位置后面的长度≥数据帧起始符的总长度
                                    if (packetsBytes.length < i + this.frameStartBytes.length) {
                                        //传入的报文总长度＜从当前索引位置开始+起始符总长度；报文后面的长度不够
                                        //后面的均不需要重复判断，判断也是长度不够，在此跳出循环
                                        break;
                                    } else {
                                        for (int j = 1; j < this.frameStartBytes.length; j++) {
                                            //如果已经有起始符中的任意一个字节不相等，表示起始符全部判等不通过，不需要再判断后面的起始符字节
                                            if (isFrameStartAllRight == true) {
                                                if (packetsBytes[i + j] == this.frameStartBytes[j]) {

                                                } else {
                                                    //如果从第二个开始的起始符不相等，跳出本循环；多个起始符中的任意一个错误均判定为当前数据帧起始错误
                                                    isFrameStartAllRight = false;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }

                                //起始符判断正确通过，判断数据帧的长度是否正确
                                if (isFrameStartAllRight == true) {
                                    //如果有指定数据帧的最小长度，先判断传入的报文长度是否足够
                                    if (this.frameMinLength > 0x0) {
                                        if (packetsBytes.length < i + this.frameMinLength) {
                                            //传入的报文总长度＜从当前索引位置开始+数据帧最小长度；报文后面的长度不够
                                            //后面的均不需要重复判断，判断也是长度不够，在此跳出循环
                                            break;
                                        }
                                    }

                                    logger.debug("分解报文数据帧，得到数据帧位置索引为[{}]-[{}]，数据帧长度为[{}]", i, endIndex, endIndex - i);
                                    //如果起始符所有字节均判等通过，数据帧的最小长度也判断通过，判定此处开始可以作为一个数据帧
                                    IotPacketsSplitResult splitPackets = new IotPacketsSplitResult();
                                    splitPackets.setStartIndex(i);
                                    splitPackets.setEndIndex(endIndex);
                                    byte[] bytes = Arrays.copyOfRange(packetsBytes, i, endIndex + 0x1);//copyOfRange方法中to的位置索引是不包含的，因此需要加1
                                    splitPackets.setBytes(bytes);

                                    result.add(splitPackets);
                                }
                            }
                        }

                        if (result.size() > 0x0)
                            return result;
                    }
                }
            }
        }

        return null;
    }

    /**
     * 解析数据帧
     *
     * @param inputBytes    数据帧报文字节数组，数组起始必须时数据值对应协议的起始符
     * @param parseBodyData 是否解析报文中的正文内容数据，为false时表示只解析数据帧基本结构框架，为true时表示解析解析结构框架和正文内容数据
     * @return qc.module.iot.protocol.IotFrameParseResult
     * @author QuCheng Tech
     * @since 2025/11/5
     */
    @Override
    public IotFrameParseResult parse(byte[] inputBytes, boolean parseBodyData) {
        //此处应该抛出异常，基类中不实现数据帧解析方法
        logger.debug("parse");

        return null;
    }

    /**
     * 解析数据帧正文内容
     *
     * @param functionBytes  功能码字节数组，长度可以为1或＞1，长度＜1时无效不进行正文内容解析
     * @param functionBytes, bodyBytes 正文内容字节数组
     * @return java.util.List<qc.module.iot.protocol.IotFrameBodyDataItem>
     * @author QuCheng Tech
     * @since 2025/11/13
     */
    @Override
    public List<IotFrameBodyDataItem> parseBodyData(byte[] functionBytes, byte[] bodyBytes) {
        logger.debug("parseBodyData");

        return null;
    }

    @Override
    public byte[] generateReplyFrame(IotFrameParseResult frameParseResult) {
        //此处应该抛出异常，基类中不实现生成回复报文方法
        logger.debug("generateReplyFrame");

        return null;
    }
}
